import base64
import os
import sys
import json
import time
import urllib.parse

import requests
from typing import Optional
from fastapi import APIRouter, Form, File, UploadFile, Header, HTTPException
from apscheduler.schedulers.asyncio import AsyncIOScheduler  # 异步调度器
from qiniu import Auth, put_data

from app.page.page_user import page_user
from models.m import User, AuthorVerification, AuthorVerificationStatus
from tool.jwt_utils import get_current_user
# 创建异步Redis连接
import redis.asyncio as redis_async

# 假设以下模型和工具已定义
redis_conn = redis_async.Redis(host='127.0.0.1', port=6379, db=0)

# 百度OCR配置
API_KEY = "LUEeCmxq5nifS0yxzBR9m5J2"
SECRET_KEY = "2TnBiY0o0ZzGRBUzvmig6liXaGsv66xg"

# 七牛云配置
QINIU_ACCESS_KEY = 'IPzQQFeLOA4fPDWzUTQQX7eJ4OcAfyzDyR3A3HN6'
QINIU_SECRET_KEY = 'hi1t3IWhYxp5H-SI7sJnFLHR9x_oUt8mFeYtuz6z'
QINIU_BUCKET = '653ef'
QINIU_DOMAIN = 'http://t4y377zln.hb-bkt.clouddn.com'

# 路由和调度器初始化
dinhsi = APIRouter()
scheduler = AsyncIOScheduler()


# ------------------------------
# 1. 百度OCR识别工具 (修改为支持URL)
# ------------------------------
def get_access_token():
    """获取百度API的access_token"""
    url = "https://aip.baidubce.com/oauth/2.0/token"
    params = {
        "grant_type": "client_credentials",
        "client_id": API_KEY,
        "client_secret": SECRET_KEY
    }
    try:
        response = requests.post(url, params=params)
        result = response.json()
        return result.get("access_token")
    except Exception as e:
        print(f"获取token失败: {str(e)}")
        return None


def ocr_id_card(img_url):
    """从URL识别身份证正面信息"""
    # 1. 下载图片
    try:
        resp = requests.get(img_url, timeout=10)
        resp.raise_for_status()
        img_data = resp.content
    except Exception as e:
        raise Exception(f"下载图片失败: {str(e)}")

    # 2. 检查大小 (最大5MB)
    if len(img_data) > 5 * 1024 * 1024:
        raise Exception("图片大小超过5MB限制")

    # 3. Base64编码
    img_base64 = base64.b64encode(img_data).decode("utf-8")

    # 4. 调用OCR接口
    access_token = get_access_token()
    if not access_token:
        raise Exception("获取access_token失败")

    url = f"https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token={access_token}"
    payload = {
        'image': img_base64,
        'id_card_side': 'front',
        'detect_quality': 'true'  # 启用质量检测
    }
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}

    try:
        response = requests.post(url, headers=headers, data=urllib.parse.urlencode(payload))
        response.raise_for_status()
        print(f"OCR结果: {response.json()}")
        return response.json()
    except Exception as e:
        raise Exception(f"OCR接口调用失败: {str(e)}")


# ------------------------------
# 2. 作者注册接口 (仅负责入队)
# ------------------------------
@page_user.post("/register", summary="注册作者", tags=["用户"])
async def register_author(
        pen_name: str = Form(...),
        introduction: Optional[str] = Form(None),
        sample_work: Optional[str] = Form(None),
        id_card_front_file: UploadFile = File(...),
        id_card_back_file: Optional[UploadFile] = File(None),
        authorization: str | None = Header(None)):
    """接收作者注册信息，上传图片并将任务入队"""
    # 1. 验证Token
    if not authorization or not authorization.lower().startswith("bearer "):
        raise HTTPException(status_code=401, detail="缺少Bearer Token")
    token = authorization.split(" ", 1)[1].strip()
    user_id = get_current_user(token)
    if not user_id or not await User.exists(id=user_id):
        raise HTTPException(status_code=401, detail="无效用户")

    # 2. 检查重复申请
    if await AuthorVerification.exists(user_id=user_id):
        raise HTTPException(status_code=400, detail="已提交过认证申请")

    # 3. 上传身份证正面到七牛云
    allowed_ext = {'.jpg', '.jpeg', '.png'}
    front_ext = os.path.splitext(id_card_front_file.filename)[1].lower()
    if front_ext not in allowed_ext:
        raise HTTPException(status_code=400, detail="正面照片仅支持jpg/png")

    # 读取文件并上传
    front_filename = f"front_{user_id}_{int(time.time())}{front_ext}"
    try:
        file_data = await id_card_front_file.read()
        q = Auth(QINIU_ACCESS_KEY, QINIU_SECRET_KEY)
        token = q.upload_token(QINIU_BUCKET, front_filename, 3600)
        ret, info = put_data(token, front_filename, file_data)
        if info.status_code != 200:
            raise Exception("上传失败")
        front_url = f"{QINIU_DOMAIN}/{front_filename}"
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"正面照片上传失败: {str(e)}")

    # 4. 上传身份证反面（可选）
    back_url = None
    if id_card_back_file:
        back_ext = os.path.splitext(id_card_back_file.filename)[1].lower()
        if back_ext not in allowed_ext:
            raise HTTPException(status_code=400, detail="反面照片仅支持jpg/png")
        back_filename = f"back_{user_id}_{int(time.time())}{back_ext}"
        try:
            file_data = await id_card_back_file.read()
            token = q.upload_token(QINIU_BUCKET, back_filename, 3600)
            ret, info = put_data(token, back_filename, file_data)
            if info.status_code == 200:
                back_url = f"{QINIU_DOMAIN}/{back_filename}"
        except Exception as e:
            print(f"反面照片上传失败: {str(e)}")  # 不阻断主流程


    verification_data = {
        "user_id": user_id,
        "pen_name": pen_name,
        "introduction": introduction,
        "sample_work": sample_work,
        "id_card_front_url": front_url,
        "id_card_back_url": back_url,
        "status": AuthorVerificationStatus.PENDING,
        "real_name":None,
        "id_card":None
    }
    # 5. 创建待验证记录
    # verification = await AuthorVerification.create(
    #     user_id=user_id,
    #     pen_name=pen_name,
    #     introduction=introduction,
    #     sample_work=sample_work,
    #     id_card_front_url=front_url,
    #     id_card_back_url=back_url,
    #     status=AuthorVerificationStatus.PENDING  # 待处理状态
    # )

    # 6. 任务入队（Redis）


    await redis_conn.lpush("id_card_verify_queue",json.dumps(verification_data))

    return {
        "code": 200,
        "message": "申请已提交，正在审核中",
        "data": {"verification_id":None}
    }


# ------------------------------
# 3. 定时任务（处理身份证验证）
# ------------------------------
async def process_verify_queue():
    """从Redis队列读取任务，执行OCR并更新状态"""
    try:

        await redis_conn.ping()  # 测试连接
    except Exception as e:
        print(f"Redis连接失败: {str(e)}")
        return

    while True:
        try:
            # 从队列取任务（阻塞10秒，无任务则退出循环）
            result = await redis_conn.brpop(["id_card_verify_queue"], timeout=10)
            if not result:
                break  # 无任务，等待下一次调度

            _, task_str = result
            task = json.loads(task_str)
            # verification_id = task["user_id"]
            user_id = task["user_id"]
            front_url = task["id_card_front_url"]

            # 获取验证记录
            verification = await AuthorVerification.get_or_none(
                user_id=user_id,
            )
            if verification:
                print(f"验证记录已存在: {user_id},")
                continue
                return {"code": 400, "message": "验证记录已存在", "data": None}

            # 执行OCR识别
            ocr_result = ocr_id_card(front_url)
            print(ocr_result)

            # 检查图片质量
            image_status = ocr_result.get("image_status", "")
            quality_errors = {
                "over_exposure": "曝光过度",
                "under_exposure": "曝光不足",
                "blurred": "图像模糊"
            }
            if image_status in quality_errors:
                raise Exception(f"照片质量问题: {quality_errors[image_status]}")
            if image_status not in ["normal", ""]:
                raise Exception(f"无效照片状态: {image_status}")

            print(ocr_result)
            # 提取姓名和身份证号
            words = ocr_result.get("words_result", {})
            real_name = words.get("姓名", {}).get("words", "").strip()
            id_card_num = words.get("公民身份号码", {}).get("words", "").strip()

            # 在创建 AuthorVerification 实例前添加验证
            if not real_name:
                raise Exception(detail="真实姓名不能为空")
            if not id_card_num or len(id_card_num) != 18:
                raise Exception("身份证号格式无效")



            verification = await AuthorVerification.create(
                user_id=user_id,
                pen_name=task["pen_name"],
                introduction=task["introduction"],
                sample_work=task["sample_work"],
                id_card_front_url=front_url,
                id_card_back_url=task["id_card_back_url"],
                status=AuthorVerificationStatus.APPROVED,
                real_name=real_name,
                id_card=id_card_num
            )
            user = await User.get_or_none(id=user_id)
            await user.update(role=2)
            await user.save()
            await verification.save()
            print(f"验证通过: 用户{user_id}, 姓名{real_name}")

        except Exception as e:
            # 验证失败，更新状态
            if 'verification' in locals() and verification:
                verification.status = AuthorVerificationStatus.REJECTED
                verification.reject_reason = str(e)  # 需要在模型中添加reason字段
                await verification.save()
            print(f"处理任务失败: {str(e)}")


# ------------------------------
# 4. 定时任务配置
# ------------------------------
def add_jobs():
    """添加定时任务"""
    # 检查任务是否已存在，避免重复添加
    if not scheduler.get_job("id_card_verifier"):
        scheduler.add_job(
            func=process_verify_queue,
            trigger="interval",
            seconds=60,  # 每30秒检查一次队列
            id="id_card_verifier",
            max_instances=1,  # 确保同一时间只有一个实例运行
            replace_existing=True  # 如果存在则替换
        )


@dinhsi.on_event("startup")
def startup_event():
    """启动时初始化调度器"""
    try:
        # 检查调度器是否已经在运行
        if not scheduler.running:
            add_jobs()
            scheduler.start()
            print("身份证验证定时任务已启动")
        else:
            print("身份证验证定时任务已在运行中")
    except Exception as e:
        print(f"启动定时任务失败: {str(e)}")


@dinhsi.on_event("shutdown")
def shutdown_event():
    """关闭时停止调度器"""
    try:
        if scheduler.running:
            scheduler.shutdown()
            print("身份证验证定时任务已停止")
    except Exception as e:
        print(f"停止定时任务失败: {str(e)}")
